//--- Projeto Domus
// interactionsource.com
// Koji Pereira, Andre Veloso e Marcos Paulo

/*
 Projeto Domus
 interactionsource.com
 Koji Pereira, Andre Veloso e Marcos Paulo

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/


package com.interactionsource.output.midi;

import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.ShortMessage;

import com.interactionsource.event.ContinuousEvent;
import com.interactionsource.event.ContinuousEventListener;
import com.interactionsource.event.TriggerEvent;
import com.interactionsource.event.TriggerEventListener;

public class EventoMIDIOut implements TriggerEventListener, ContinuousEventListener {

	public static final int MIDI_CC_EVENT = 0;
	public static final int MIDI_CC_TOGLLE_EVENT = 1;
	public static final int MIDI_NOTE_EVENT = 2;
	public static final int MIDI_PITCH_BEND_EVENT = 3;

	public static final float MAX_VALUE_FLOAT_TO_MIDI = 1.5f;
	public static final float MIN_VALUE_FLOAT_TO_MIDI = -1.5f;

	public static final String TIPOS_MIDI[] = { "MIDI_CC_EVENT", 
		"MIDI_CC_TOGLLE_EVENT",
	"MIDI_NOTE_EVENT" ,
		"MIDI_PITCH_BEND_EVENT"};


	private int tipo;

	private int canal;

	private int data1;

	private int data2;

	private DispositivoMIDIOut dispositivoMIDIOut;

	public EventoMIDIOut(DispositivoMIDIOut dispositivoMIDIOut, int tipo, int canal, int dado) {

		this.tipo = tipo;

		if (canal >=16) { this.canal = 15; }
		else if (canal <0) { this.canal = 0; }
		else {  this.canal = canal-1;  }


		this.data1 = dado;

		this.dispositivoMIDIOut = dispositivoMIDIOut;
	}


	/**
	 * @return the tipo
	 */
	public int getTipo() {
		return tipo;
	}

	/**
	 * @param tipo the tipo to set
	 */
	public void setTipo(int tipo) {
		this.tipo = tipo;
	}

	/**
	 * @return the canal
	 */
	public int getCanal() {
		return canal;
	}

	/**
	 * @param canal the canal to set
	 */
	public void setCanal(int canal) {
		this.canal = canal;
	}

	/**
	 * @return the data1
	 */
	public int getData1() {
		return data1;
	}

	/**
	 * @param data1 the data1 to set
	 */
	public void setData1(int data1) {
		this.data1 = data1;
	}

	/**
	 * @return the data2
	 */
	public int getData2() {
		return data2;
	}

	/**
	 * @param data2 the data2 to set
	 */
	public void setData2(int data2) {
		this.data2 = data2;
	}


	/**
	 * @return the dispositivoMIDIOut
	 */
	public DispositivoMIDIOut getDispositivoMIDIOut() {
		return dispositivoMIDIOut;
	}

	/**
	 * @param dispositivoMIDIOut the dispositivoMIDIOut to set
	 */
	public void setDispositivoMIDIOut(DispositivoMIDIOut dispositivoMIDIOut) {
		this.dispositivoMIDIOut = dispositivoMIDIOut;
	}

	/* (non-Javadoc)
	 * @see com.interactionsource.event.TriggerEventListener#triggerEventReceived(com.interactionsource.event.TriggerEvent)
	 */
	public void triggerEventReceived(TriggerEvent event) {
		//System.out.println("EventoMIDIOut - Recebi TriggerEvent: Valor: " + 
		//		event.getValue() );

		ShortMessage msg = new ShortMessage();

		int midiValue = 0;

		try {

			if (this.tipo == MIDI_CC_TOGLLE_EVENT) {

				//Transforma o evento recebido na gama MIDI permitida de 0 a 127
				midiValue = (int)(event.getValue()*127);
				
				msg.setMessage(ShortMessage.CONTROL_CHANGE, this.canal, this.data1, midiValue);
			}

			if (this.tipo == MIDI_NOTE_EVENT) {

				//Transforma o evento recebido na gama MIDI permitida de 0 a 127
				midiValue = (int)(event.getValue()*127);
				
				if (midiValue == 127)
					msg.setMessage(ShortMessage.NOTE_ON, this.canal, this.data1, midiValue);
				else if (midiValue == 0)
					msg.setMessage(ShortMessage.NOTE_OFF, this.canal, this.data1, midiValue);
			}

			this.dispositivoMIDIOut.enviaMsgAgora(msg);

		} catch (InvalidMidiDataException e) {

			System.out.println("EventoMIDIOut - Valor fora de escala valor input: " + 
					event.getValue() + " - valor MIDI: " + midiValue);
		}

	}


	/* (non-Javadoc)
	 * @see com.interactionsource.event.ContinuousEventListener#continuousEventReceived(com.interactionsource.event.ContinuousEvent)
	 */
	public void continuousEventReceived(ContinuousEvent event) {


		ShortMessage msg = new ShortMessage();
		
		int midiValue = 0;

		try {

			if (this.tipo == MIDI_CC_EVENT) {
				
				//Converte o valor para a gama midi permitida
				midiValue = mapValue( event.getValue(), 
						MIN_VALUE_FLOAT_TO_MIDI, 
						MAX_VALUE_FLOAT_TO_MIDI, 
						0, 
						127 );			

				msg.setMessage(ShortMessage.CONTROL_CHANGE, this.canal, data1, midiValue);
				
			} else 	if (this.tipo == MIDI_PITCH_BEND_EVENT) {
				
				//Converte o valor para a gama midi permitida
				midiValue = mapValue( event.getValue(), 
						MIN_VALUE_FLOAT_TO_MIDI, 
						MAX_VALUE_FLOAT_TO_MIDI, 
						0, 
						127 );			

				msg.setMessage(ShortMessage.PITCH_BEND, this.canal, data1, midiValue);
			}

			
			
			//System.out.println("EventoMIDIOut - Valor input: " + 
			//		event.getValue()+ " - valor MIDI: " + midiValue);

			this.dispositivoMIDIOut.enviaMsgAgora(msg);

		} catch (InvalidMidiDataException e) {

			//e.printStackTrace();
			System.out.println("EventoMIDIOut - ERRO - Valor fora de escala valor input: " + 
					event.getValue() + " - valor MIDI: " + midiValue);

		}

	}

	public String toString(){
		String saida = "MIDI"   
			+ ";" + this.dispositivoMIDIOut.getDispositivoMIDIName()
			+ ";" + TIPOS_MIDI[this.tipo] 
			                   + ";" + this.canal 
			                   + ";" + this.data1;

		return saida;

	}

	/**
	 * @param x
	 * @param in_min
	 * @param in_max
	 * @param out_min
	 * @param out_max
	 * @return
	 */
	private int mapValue(float x, float in_min, float in_max, int out_min, int out_max)
	{

		return (int)(  (x - in_min) * (out_max - out_min) / (in_max - in_min) + (out_min) );
	}

}
